/**
    Copyright (c) 2010 yakiimo02
    Distributed under the New BSD License.
    See included license.txt or http://www.yakiimo3d.com/NewBSDLicense.txt
**/

/**
    @file   
    @brief  Reduce 1D buffer to a single value.
            Based on DirectX SDK's HDRToneMappingCS11 sample's ReduceToSingle.hlsl 
    @author yakiimo02
    @date    2010/03/07
*/

StructuredBuffer<float2> Input        : register( t0 );
RWStructuredBuffer<float2> Result    : register( u0 );

cbuffer cbCS : register( b0 )
{
    uint4    g_param;   // g_param.x is the actual elements contained in Input
                        // g_param.y is the x dimension of the Dispatch call
};

#define groupthreads 128
groupshared float2 accum[groupthreads];

[numthreads(groupthreads,1,1)]
void CSMain( uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex )
{
    if ( DTid.x < g_param.x ) {
        accum[GI] = Input[DTid.x];
    }
    else {
        accum[GI] = float2( 0.0, 0.0 );
    }
    
    // Parallel reduction algorithm follows 
    GroupMemoryBarrierWithGroupSync();
    if ( GI < 64 ) { 
        accum[GI].x += accum[64+GI].x;  
        accum[GI].y = max( accum[GI].y, accum[64+GI].y );  
    }

    GroupMemoryBarrierWithGroupSync();
    if ( GI < 32 ) {   
        accum[GI].x += accum[32+GI].x;
        accum[GI].y = max( accum[GI].y, accum[32+GI].y );  
    }

    GroupMemoryBarrierWithGroupSync();
    if ( GI < 16 ) {
        accum[GI].x += accum[16+GI].x;
        accum[GI].y = max( accum[GI].y, accum[16+GI].y );  
    }

    GroupMemoryBarrierWithGroupSync();
    if ( GI < 8 ) {
        accum[GI].x += accum[8+GI].x;
        accum[GI].y = max( accum[GI].y, accum[8+GI].y );  
    }        

    GroupMemoryBarrierWithGroupSync();
    if ( GI < 4 ) {
        accum[GI].x += accum[4+GI].x;
        accum[GI].y = max( accum[GI].y, accum[4+GI].y );  
    }

    GroupMemoryBarrierWithGroupSync();
    if ( GI < 2 ) {
        accum[GI].x += accum[2+GI].x;
        accum[GI].y = max( accum[GI].y, accum[2+GI].y );  
    }

    GroupMemoryBarrierWithGroupSync();
    if ( GI < 1 ) {
        accum[GI].x += accum[1+GI].x;
        accum[GI].y = max( accum[GI].y, accum[1+GI].y );  
    }
    
    if ( GI == 0 )
    {  
        Result[Gid.x] = accum[0];
    }
}
